package tech.xs.common.framework.init.db;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.jdbc.ScriptRunner;
import org.springframework.core.annotation.Order;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
import tech.xs.common.framework.domain.exception.InitDataBaseException;
import tech.xs.common.framework.init.InitExecute;
import tech.xs.common.framework.init.db.domain.model.DataConfig;
import tech.xs.common.framework.init.db.domain.properties.DatasourceProperties;
import tech.xs.common.framework.init.db.sql.DefaultSqlBuilder;
import tech.xs.common.framework.init.db.sql.MqSqlSqlBuilder;
import tech.xs.common.framework.init.db.sql.SqlBuilder;
import tech.xs.common.framework.util.SpringUtil;

import javax.annotation.Resource;
import javax.sql.DataSource;
import java.io.IOException;
import java.io.StringReader;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;

/**
 * 初始化表结构
 */
@Slf4j
@Component
@Order(200)
public class InitDataBase implements InitExecute {

    private static final String DDL_FILE_PATH = "classpath:init/db/ddl/**/*.sql";

    private static final String DATA_JSON_FILE_PATH = "classpath:init/db/data/**/*.json";

    @Resource
    private JdbcTemplate jdbcTemplate;
    @Resource
    private DataSource dataSource;
    @Resource
    private DatasourceProperties datasourceProperties;

    @Override
    public void execute() throws Exception {
        if (datasourceProperties.getIsInitDataStructure()) {
            initDataBaseStructure();
        }
        if (datasourceProperties.getIsInitData()) {
            initDataBaseData();
        }
    }

    @Override
    public boolean isErrorStop() {
        return true;
    }

    private void initDataBaseStructure() throws SQLException {
        log.info("[初始化数据结构][开始]");
        List<String> contentList = SpringUtil.getResourceFileContentList(DDL_FILE_PATH);
        if (CollUtil.isEmpty(contentList)) {
            return;
        }
        Connection conn = dataSource.getConnection();
        ScriptRunner runner = new ScriptRunner(conn);
        runner.setAutoCommit(true);
        runner.setStopOnError(true);
        runner.setLogWriter(null);
        for (String ddl : contentList) {
            log.info("初始化数据库结构 执行SQL:" + ddl);
            if (StrUtil.isBlank(ddl)) {
                continue;
            }
            runner.runScript(new StringReader(ddl));
        }
        conn.close();
        log.info("[初始化数据结构][结束]");
    }

    private void initDataBaseData() throws IOException, SQLException {
        log.info("[初始化数据库数据][开始]");
        List<DataConfig> dataConfigList = getDataConfigList();
        for (DataConfig dataConfig : dataConfigList) {
            executeItem(dataConfig);
        }
        log.info("[初始化数据库数据][结束]");
    }

    /**
     * 执行单个配置文件
     */
    private void executeItem(DataConfig config) throws SQLException {
        log.info("[初始化数据库数据][开始] {} {}", config.getTableName(), config.getModel());
        switch (config.getModel()) {
            case IGNORE_INSERT: {
                executeIgnoreInsertData(config);
                break;
            }
            default: {
                throw new InitDataBaseException("[model]错误:" + config.getModel());
            }
        }
        log.info("[初始化数据库数据][结束] {} {}", config.getTableName(), config.getModel());
    }

    private void executeIgnoreInsertData(DataConfig config) throws SQLException {
        List<Map<String, Object>> dataList = config.getData();
        SqlBuilder sqlBuilder = getSqlBuilder();
        sqlBuilder.table(config.getTableName());
        sqlBuilder.uniqueField(config.getUniqueField());
        sqlBuilder.uniqueField(config.getUniqueFieldArray());
        for (Map<String, Object> data : dataList) {
            if (!isDataExist(sqlBuilder, data)) {
                insertData(sqlBuilder, data);
            }
        }
    }

    private SqlBuilder getSqlBuilder() throws SQLException {
        try (Connection connection = dataSource.getConnection()) {
            String driverName = connection.getMetaData().getDriverName().toUpperCase();
            if (driverName.contains("MYSQL")) {
                return new MqSqlSqlBuilder();
            }
            return new DefaultSqlBuilder();
        } catch (Exception e) {
            log.error("构建SQL异常", e);
            throw e;
        }
    }

    /**
     * 判断数据是否存在数据库中
     */
    private boolean isDataExist(SqlBuilder sqlBuilder, Map<String, Object> data) {
        String sql = sqlBuilder.buildExistSql(data);
        log.info("[初始化数据库数据][判断数据是否存在][执行SQL]: " + sql);
        return jdbcTemplate.queryForObject(sql, Integer.class) > 0;
    }

    private void insertData(SqlBuilder sqlBuilder, Map<String, Object> data) {
        String sql = sqlBuilder.buildInsertSql(data);
        log.info("[初始化数据库数据][执行SQL]: " + sql);
        jdbcTemplate.execute(sql);
    }

    /**
     * 获取配置文件列表,并根据配置文件中的ordered字段从小到大排序
     */
    private List<DataConfig> getDataConfigList() throws IOException {
        List<DataConfig> dataConfigList = new ArrayList<>();
        List<String> contentList = SpringUtil.getResourceFileContentList(DATA_JSON_FILE_PATH);
        for (String content : contentList) {
            log.info("初始化数据:" + content);
            ObjectMapper objectMapper = new ObjectMapper();
            DataConfig dataConfig = objectMapper.readValue(content, DataConfig.class);
            if (dataConfig != null) {
                dataConfigList.add(dataConfig);
            }
        }
        dataConfigList.sort(Comparator.comparingInt(DataConfig::getOrdered));
        return dataConfigList;
    }

}
